package Methods;

import java.util.Arrays;


import function.Function;
import function.Parser;

public class FalsePosition implements Methods {
	// output constants
	private static final byte k = 0, a = 1, c = 2, b = 3, fc = 4;
	// input constants
	private static final byte func = 0, pointOne = 1, pointTwo = 2, delta = 3,
			epsilon1 = 4;

	double[][] result;
	Function function;
	double point1;
	double point2;
	double tolerance;
	double epsilon;

	public FalsePosition(String f, double p1, double p2) throws Exception {
		this.function = Parser.toFunction(f);
		this.point1 = p1;
		this.point2 = p2;
		this.tolerance = 0.001;
		this.epsilon = 1E-8;

		this.result = new double[30][5];
		// Initially fill array with -1
		for (double[] a : result)
			Arrays.fill(a, -1);

	}

	public FalsePosition(String input[]) throws Exception {

		this.function = Parser.toFunction(input[func]);
		this.point1 = Double.parseDouble(input[pointOne]);
		this.point2 = Double.parseDouble(input[pointTwo]);
		this.tolerance = Double.parseDouble(input[delta]);
		this.epsilon = Double.parseDouble(input[epsilon1]);

		this.result = new double[30][5];
		// Initially fill array with -1
		for (double[] a : result)
			Arrays.fill(a, -1);
	}

	private void iterate(int index, double point1, double point2) {

		result[index][k] = index;// k
		result[index][a] = point1;
		result[index][b] = point2;
		double dx = (function.evaluate(point2) * (point2 - point1))
				/ (function.evaluate(point2) - function.evaluate(point1));
		double c1 = point2 - dx;
		result[index][c] = c1;
		result[index][fc] = function.evaluate(c1);

		if (Math.min(Math.abs(dx), result[index][c] - result[index][a]) < tolerance
				|| Math.abs(result[index][fc]) < epsilon) {
			// System.out.println(function.evaluate((point2+point1)/2));
			return;
		}

		if (function.evaluate(c1) == 0)
			return;
		else if (function.evaluate(point1) * function.evaluate(c1) < 0)
			iterate(index + 1, point1, c1);
		else {
			iterate(index + 1, c1, point2);
		}
	}

	@Override
	public String[] getInputNames() {
		return new String[] { "Function", "Point a", "Point b", "Tolerance",
				"Epsilon" };
	}

	@Override
	public String[] getOutputAddresses() {

		return new String[] { "k", "a", "c", "b", "F(c)" };
	}

	@Override
	public String[] getSampleInput() {
		// TODO Auto-generated method stub
		return new String[] { "x*sin(x)-1", "0", "2", ".01" };
	}

	@Override
	public double[][] solve() throws Exception {
		if (function.evaluate(point1) * function.evaluate(point2) > 0)
			throw new Exception("Invalid Input: f(a)*f(b)>0");
		iterate(0, point1, point2);

		// trim
		int i = 0;
		for (i = 0; i < result.length; i++) {
			if (result[i][0] == -1)
				break;
		}
		double[][] trim = Arrays.copyOf(result, i);
		return trim;
	}

	@Override
	public double getError() {
		return 0;
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		/*
		 * FalsePosition testFalsePosition = new FalsePosition(new String[] {
		 * "x*sin(x)-1", "0", "2", ".001","1E-8" });
		 */

		FalsePosition testFalsePosition = new FalsePosition("x*sin(x)-1", 0, 2);

		double[][] testResult = testFalsePosition.solve();

		for (double[] a : testResult)
			System.out.println(Arrays.toString(a));

	}

}